﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Go;

namespace fhs
{
    class AssemblyLine
    {
        abstract public class Product
        {
        }

        private int _buffSize;
        private shared_strand _strand;
        private LinkedList<tuple<Func<Product, Task<Product>>, chan<Product>>> _line;

        public AssemblyLine(shared_strand strand, int buffSize)
        {
            _strand = strand;
            _buffSize = buffSize;
            _line = new LinkedList<tuple<Func<Product, Task<Product>>, chan<Product>>>();
            _line.AddLast(new tuple<Func<Product, Task<Product>>, chan<Product>>(null, chan<Product>.make(_strand, _buffSize)));
        }

        public chan<Product> First
        {
            get
            {
                return _line.First.Value.value2;
            }
        }

        public chan<Product> Last
        {
            get
            {
                return _line.Last.Value.value2;
            }
        }

        public ValueTask<chan_send_wrap> FeedIn(Product product)
        {
            return First.send(product);
        }

        public ValueTask<chan_recv_wrap<Product>> FeedOut()
        {
            return Last.receive();
        }

        public void Close()
        {
            First.close();
        }

        public void CloseAll()
        {
            foreach (tuple<Func<Product, Task<Product>>, chan<Product>> station in _line)
            {
                station.value2.close();
            }
        }

        public void AddStation(Func<Product, Task<Product>> station)
        {
            AddStation(_strand, station);
        }

        public void AddStation(shared_strand strand, Func<Product, Task<Product>> station)
        {
            _line.AddLast(tuple.make(station, chan<Product>.make(_strand, _buffSize)));
            generator.go(strand, functional.bind(WorkStation, _line.Last));
        }

        private async Task WorkStation(LinkedListNode<tuple<Func<Product, Task<Product>>, chan<Product>>> station)
        {
            try
            {
                while (true)
                {
                    chan_recv_wrap<Product> newWork = await station.Previous.Value.value2.receive();
                    if (chan_state.closed == newWork.state)
                    {
                        break;
                    }
                    if (chan_state.closed == await station.Value.value2.send(await station.Value.value1(newWork.msg)))
                    {
                        break;
                    }
                }
            }
            finally
            {
                station.Previous.Value.value2.close();
                station.Value.value2.close();
            }
        }
    }
}
